package client;

import java.io.*;
import java.net.*;

import fileServer.FileServerApplication;
import cmm.ConnectionInformation;
import filePackets.*;

public class FileClient extends Client implements Runnable {
	// DEFINES
	private static String ProxyAddress;
	// private variables
	private static server.Logger LogFile;
	private static ConnectionInformation ProxyConnInfo;
	private static ConnectionInformation ServerConnInfo;
	private static Thread ClientThread;

	private String ClientString;
	private int SerialNumber;
	private String LogFileName;
	private Short ProxyPort;
	private Integer FileNumber;
	private String ProxyIP;
	
	/**
	 * @param args
	 */
	
	private void LogEntry(String msg){
		LogFile.AddEntry(ClientString, msg);
	}
	
	public FileClient(int SN, short PP, String PIP, Integer FN){
		SerialNumber = SN;
		ProxyPort = PP;
		ProxyIP = new String(PIP);
		FileNumber = new Integer(FN);
		ClientString = "Client "+SerialNumber;
		LogFileName = "Client_"+SerialNumber+".log";
		ClientThread = new Thread(this);
		ClientThread.start();		
	}
	
	/**
	 * This is the main function of the FileClient application
	 * 
	 * @param args command line argumets for the Client
	 * @throws  
	 */
	public static void main(String[] args) {
		
				
		FileClient MyRef;
		if (args.length < 3){
			System.out.println("Error - wrong number of command line parameters");
			System.out.println("Usage: prog <proxy-addr> <proxy-port> <file-number> [Serial Number]");			
			return;
		}
		
		ProxyAddress = args[0];
		
		short ProxyPort;
		try{
			ProxyPort = new Short(args[1]);
		}catch (NumberFormatException e2){
			e2.printStackTrace();
			System.out.println("Error - second command line argument must be an integer number (FileNumber)");
			return;
		}
		
		Integer FileNumber;
		try {
			FileNumber = new Integer(args[2]);
		} catch (NumberFormatException e2) {
			e2.printStackTrace();
			System.out.println("Error - third command line argument must be an integer number (FileNumber)");
			return;
		}
		int SN = 0;
		if (args.length == 4){
			SN = (int)new Integer(args[3]);
		}
		
		MyRef = new FileClient(SN, ProxyPort, ProxyAddress, FileNumber);

		try {
			ClientThread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("Client Thread was terminated");
			return;
		}
		System.out.println("Client "+SN+" exited");
		return;		
		
		
	}

	public void run() {
		
		LogFile = new server.Logger();
		
		try {
			LogFile.OpenLog(LogFileName);
		} catch (FileNotFoundException e) {
			System.out.println("cannot create log file");
			e.printStackTrace();
			return;
		}
		LogEntry("Info - Client started");
		
		ProxyConnInfo = new ConnectionInformation();
		try {
			ProxyConnInfo.putIP(InetAddress.getByName(ProxyAddress));
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
			LogEntry("Could not resolve Proxy Address "+ProxyAddress);
			return;
		}
		ProxyConnInfo.putPort(ProxyPort);
				
		try {
			ServerConnInfo = GetServerInfo(ProxyConnInfo);
		} catch (Exception e) {
			LogEntry("Error - Communicaiton with Proxy failed !");
			e.printStackTrace();
			return;
		} 
		LogEntry("Info - obtained connection information for server "+ServerConnInfo.getIP());
		DatagramSocket FromServer;
		try {
			FromServer = new DatagramSocket();
		} catch (SocketException e) {
			e.printStackTrace();
			LogEntry("Could not create a datagram socket for incoming traffic");
			return;
		}
				
		ConnectionInformation MyConnInfo = new ConnectionInformation();
		
		try {
			MyConnInfo.putIP(InetAddress.getLocalHost());
		} catch (UnknownHostException e2) {
			LogEntry("Error - failed to obtain local address");
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		short sh = new Integer(FromServer.getLocalPort()).shortValue();
		MyConnInfo.putPort(sh);
		
						
		NewSessionPacket NewSessionReq = new NewSessionPacket();
		NewSessionReq.setClientData(MyConnInfo);
		NewSessionReq.setRequestedFile(FileNumber);
		LogEntry("sending request to Server: requested file number is "+FileNumber);
		LogEntry("my connection information is: IP="+MyConnInfo.getIP()+" Port="+MyConnInfo.getPort());

		filePackets.Sender mySender = new Sender();
		try {
			mySender.WriteObjectTo(NewSessionReq, FromServer, ServerConnInfo.getIP(), ServerConnInfo.getPort());
		} catch (Exception e2) {
			LogEntry("Error writing to socket"); 
			e2.printStackTrace();
			return;
		}

		filePackets.Receiver myReceiver = new Receiver();
		String myOutputFile = new String("myfile"+SerialNumber+".out");
		LogEntry("opening local output file: "+ myOutputFile);
		FileOutputStream OutFile;
		try {
			OutFile = new FileOutputStream(myOutputFile);
		} catch (FileNotFoundException e1) {
			LogEntry("error creating file for output");
			e1.printStackTrace();
			return;
		}
		LogEntry("starting to receive file...");
		boolean done;
		int ExpectedBlockNumber = 0;
		DataPacket InData = new DataPacket();
		do{
			
			try {
				InData = (DataPacket)myReceiver.ReadObjectFrom(FromServer);
			} catch (IOException e1) {
				LogEntry("error reading from socket");
				e1.printStackTrace();
			}

			System.out.println("Client "+SerialNumber+": Got block number "+InData.getBlockNumber());
			LogEntry("Client "+SerialNumber+": Got block number "+InData.getBlockNumber());
			if (InData.getBlockNumber() == ExpectedBlockNumber ){
				try {
					OutFile.write(InData.getBlockData(), 0, InData.getValidBytes());
				} catch (IOException e) {
					LogEntry("Error writing block to file");
					e.printStackTrace();
					return;
				}
				ExpectedBlockNumber++;
			}
			else{
				LogEntry("expected to get block number "+ExpectedBlockNumber+" instead got block number "+InData.getBlockNumber()+" - ignoring");
			}				
			done = (InData.getMaxBlocks() == InData.getBlockNumber() +1);						
		}while (!done);
		try {
			OutFile.close();
		} catch (IOException e) {
			LogEntry("Error closing file");
			e.printStackTrace();
			return;
		}
		LogEntry("Client "+SerialNumber+": done receiving file");
		System.out.println("Client: done receiving file");

		
	}
}
